home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 21 / Mac Magazin and MacEasy Magazine CD - Issue 21.iso / Wissenschaft & Technik / yorick12vr1-nofpu folder / include / netcdf.i < prev    next >
Text File  |  1995-07-26  |  19KB  |  667 lines

  1. /*
  2.    NETCDF.I
  3.    Yorick procedures to open a netCDF file
  4.  
  5.    $Id: netcdf.i,v 1.1 1993/08/27 18:50:06 munro Exp $
  6.  */
  7. /*    Copyright (c) 1994.  The Regents of the University of California.
  8.                     All rights reserved.  */
  9.  
  10. /* Note: I don't use netCDF files, so there are probably still some
  11.          bugs in this code (DHM).  */
  12.  
  13. local netcdf;
  14. /* DOCUMENT nc_open, nc_create, nc_vardef, nc_enddef, nc_addrec
  15.      are the main routines to read and write netCDF files.
  16.  
  17.      The ordinary openb function will also open netCDF files.
  18.  
  19.      Writing a netCDF file is more problematic in Yorick, since
  20.      you must define the entire file structure before you write
  21.      any data.  Therefore, the nc_create call returns only a
  22.      "token" for nc_vardef, which you use to declare variables
  23.      in the file.  When you are done declaring variables, you
  24.      call nc_enddef, which returns an ordinary Yorick file object.
  25.      You can then write data to the file (with f.var=value or
  26.      save,f,var).  To add a record, you must use nc_addrec instead
  27.      of add_record (nc_addrec updates the record count in the file).
  28.  */
  29.  
  30. /* ------------- netCDF interface --------------------------------- */
  31.  
  32. func nc_open(filename, mode)
  33. /* DOCUMENT f= nc_open(filename, mode)
  34.      opens a netCDF file FILENAME for reading or update as specified
  35.      by MODE, which defaults to "rb".  Attributes and dimension names
  36.      can be found in the three external variables nc_dims (an array of
  37.      type NC_dim), nc_attrs (an array of type NC_attr), and nc_vars
  38.      (an array of type NC_var) after this call.
  39.      MODE should be either "rb" or "r+b"; nothing else makes sense.
  40.    SEE ALSO: nc_create, nc_enddef, nc_attribute, nc_dimsof
  41.  */
  42. {
  43.   if (is_void(mode)) mode= "rb";
  44.   f= open(filename, mode);
  45.   if (raw_not_cdf(f)) return [];
  46.   return f;
  47. }
  48.  
  49. func raw_not_cdf(f)
  50. {
  51.   i= array(char, 4);
  52.   _read, f, 0, i;
  53.   if (string(&i)!="CDF\001") return 1;  /* test magic number */
  54.  
  55.   xdr_primitives, f;
  56.  
  57.   numrecs= long(0);
  58.   _read, f, 4, numrecs;
  59.  
  60.   /* Each of the major components of a netCDF file contains information
  61.      not used by Yorick.  Specifically, dimensions are named, the file
  62.      may have attributes, and any variable contained in the file may
  63.      have attributes.  After a call to nc_open, this additional
  64.      information is stored in the external variable nc_file.  */
  65.   extern nc_file;
  66.  
  67.   pf= print(f);
  68.   name= dir= "";
  69.   sread, pf(where(strmatch(pf,"binary stream:"))), dir, name,
  70.     format= "%s binary stream: %s";
  71.   sread, pf(where(strmatch(pf,"In directory:"))), dir,
  72.     format= " In directory: %s";
  73.  
  74.   address= 8;
  75.   nc_dims= NC_ReadArray(f, address);
  76.   nc_attrs= NC_ReadArray(f, address);
  77.   nc_vars= NC_ReadArray(f, address);
  78.   nc_file= NC_file(numrecs=numrecs, dims=&nc_dims, attrs=&nc_attrs,
  79.            vars=&nc_vars, filename=dir+name);
  80.  
  81.   if (_nc_declare(f, nc_file)) {
  82.     /* check for presence of a file family */
  83.     while (!add_next_file(f)) {
  84.       i= array(char, 4);
  85.       _read, f, 0, i;
  86.       if (string(&i)!="CDF\001") break;  /* test magic number */
  87.       _read, f, 4, numrecs;
  88.       if (!numrecs) continue;
  89.       nc_file.numrecs= numrecs;
  90.       address= 8;
  91.       dims= NC_ReadArray(f, address);
  92.       attrs= NC_ReadArray(f, address);
  93.       vars= NC_ReadArray(f, address);
  94.       if (numberof(dims)!=numberof(nc_dims) ||
  95.       anyof(dims.size!=nc_dims.size) ||
  96.       !_nc_declare(f, nc_file, vars)) break;
  97.     }
  98.   }
  99.  
  100.   jr, f, 1;
  101.   return 0;
  102. }
  103.  
  104. func _nc_declare(f, ncf, check_vars)
  105. {
  106.   nc_dims= *ncf.dims;
  107.   nc_vars= *ncf.vars;
  108.   numrecs= ncf.numrecs;
  109.  
  110.   if (!is_void(check_vars)) {
  111.     if (numberof(nc_vars)!=numberof(check_vars) ||
  112.     anyof(nc_vars.name!=check_vars.name) ||
  113.     anyof(nc_vars.type!=check_vars.type) ||
  114.     anyof(nc_vars.len!=check_vars.len) ||
  115.     anyof(nc_vars.address!=check_vars.address)) return 0;
  116.   }
  117.  
  118.   /* check whether there is an unlimited (history) dimension */
  119.   if (is_void(nc_dims)) unlimited= -1;
  120.   else {
  121.     unlimited= (nc_dims.size==0);
  122.     if (noneof(unlimited)) unlimited= -1;
  123.     else unlimited= where(unlimited)(0);
  124.   }
  125.  
  126.   /* first pass sets non-history variables */
  127.   type_names= ["char", "char", "short", "long", "float", "double"];
  128.   nvars= numberof(nc_vars);
  129.   if (nvars) nonRecord= array(int, nvars);
  130.   else nonRecord= 1;
  131.   for (i=1 ; i<=nvars ; ++i) {
  132.     var= nc_vars(i);
  133.     dimlist= *var.dimlist;
  134.     if (numberof(dimlist)) {
  135.       dimlist+= 1;
  136.       if (dimlist(1)==unlimited) continue;  /* record variable */
  137.       dimlist= grow([numberof(dimlist)], nc_dims.size(dimlist(0:1:-1)));
  138.     } else {
  139.       dimlist= [0];
  140.     }
  141.     nonRecord(i)= 1;
  142.     if (is_void(check_vars))
  143.       add_variable, f, var.address, var.name, type_names(var.type), dimlist;
  144.   }
  145.  
  146.   /* second pass sets history variables */
  147.   if (nallof(nonRecord)) {
  148.     if (is_void(check_vars))
  149.       add_record, f;
  150.     recList= where(!nonRecord);
  151.     ha= min(nc_vars.address(recList));
  152.     time_address= 0;
  153.     time_type= 0;
  154.     for (i=1 ; i<=nvars ; ++i) {
  155.       if (nonRecord(i)) continue;
  156.       var= nc_vars(i);
  157.       dimlist= *var.dimlist;
  158.       ++dimlist;
  159.       if (numberof(dimlist) > 1) {
  160.     dimlist= grow([numberof(dimlist)-1], nc_dims.size(dimlist(0:2:-1)));
  161.       } else {
  162.     dimlist= [0];
  163.     if (var.name=="time" && (var.type==5 || var.type==6)) {
  164.       time_address= var.address;
  165.       time_type= var.type;
  166.     }
  167.       }
  168.       if (is_void(check_vars))
  169.     add_variable, f, var.address-ha,
  170.                      var.name, type_names(var.type), dimlist;
  171.     }
  172.  
  173.     /* Note: If there is exactly one record variable, then the records
  174.        need not begin on a 4-byte boundary.  This is only an issue for
  175.        a single record variable of type char or short.  */
  176.  
  177.     if (numberof(recList)>1 || var.type>3) {
  178.       hs= sum(nc_vars.len(recList));
  179.     } else {
  180.       hs= (var.type==3? 2 : 1);
  181.       for (i=2 ; i<=1+dimlist(1) ; ++i) hs*= dimlist(i);
  182.     }
  183.     if (numrecs) {
  184.       if (time_address) {
  185.     if (time_type==6) time= 0.0;
  186.     else time= 0.0f;
  187.     times= array(0.0, numrecs);
  188.     for (i=1 ; i<=numrecs ; ++i) {
  189.       _read, f, time_address, time;
  190.       time_address+= hs;
  191.       times(i)= time;
  192.     }
  193.     add_record, f, times, , ha+hs*indgen(0:numrecs-1);
  194.       } else {
  195.     add_record, f, , , ha+hs*indgen(0:numrecs-1);
  196.       }
  197.     }
  198.  
  199.     return 1;
  200.   }
  201.  
  202.   return 0;
  203. }
  204.  
  205. func nc_create(filename)
  206. /* DOCUMENT ncf= nc_create(filename)
  207.      creates a netCDF file FILENAME.
  208.      After this call, use nc_vardef to declare the netCDF variables.
  209.      Then use nc_enddef to write the netCDF self-descriptive
  210.      information.  Only after this are you free to actually write data.
  211.  
  212.    SEE ALSO: nc_open, nc_vardef, nc_enddef, nc_addrec,
  213.              nc_attribute, nc_dimsof
  214.  */
  215. {
  216.   return NC_file(filename=filename);
  217. }
  218.  
  219. func nc_vardef(ncf, name, type, dims, template=, record=)
  220. /* DOCUMENT nc_vardef, ncf, name, type, dims, record=0/1
  221.        -or- nc_vardef, ncf, name, type, record=0/1
  222.        -or- nc_vardef, ncf, name, template=template, record=0/1
  223.      define a variable in the NCF (returned by nc_create) with name
  224.      NAME, type TYPE (as returned by typeof or structof), and dimensions
  225.      DIMS (as returned by dimsof).  The template= keyword may be used
  226.      instead of type and dims; the type and dims will be those of the
  227.      TEMPLATE.  If dims is not specified, a scalar is assumed.  If the
  228.      record= keyword is present and non-zero, the variable is a record
  229.      variable; otherwise it is a non-record variable.
  230.  
  231.    SEE ALSO: nc_create, nc_enddef, nc_addrec
  232.  */
  233. {
  234.   nc_vars= *ncf.vars;
  235.   if (!is_void(nc_vars) &&
  236.       anyof(nc_vars.name==name)) error, "duplicate netCDF name: "+name;
  237.  
  238.   if (!is_void(template)) {
  239.     type= typeof(template);
  240.     dims= dimsof(template);
  241.   } else {
  242.     if (!is_array(type)) {
  243.       if (type==char) type= "char";
  244.       else if (type==short) type= "short";
  245.       else if (type==long) type= "long";
  246.       else if (type==float) type= "float";
  247.       else if (type==double) type= "double";
  248.       else type= "illegal";
  249.     }
  250.     if (is_void(dims)) dims= [0];
  251.   }
  252.   if (type=="int") type= "long";
  253.   list= where(type==["char","char","short","long","float","double"]);
  254.   if (!numberof(list)) error, "illegal netCDF data type: "+type;
  255.   type= list(1);
  256.   len= [1, 1, 2, 4, 4, 8](type);
  257.  
  258.   if (record) {
  259.     grow, dims, [0];
  260.     dims(1)+= 1;
  261.   }
  262.  
  263.   if (numberof(dims)>1) {
  264.     nc_dims= *ncf.dims;
  265.     ndims= dims(1);
  266.     changed= 0;
  267.     for (i=2 ; i<=1+ndims ; ++i) {
  268.       if (dims(i)) len*= dims(i);
  269.       if (numberof(nc_dims)) {
  270.     list= where(dims(i)==nc_dims.size);
  271.     if (numberof(list)) {
  272.       dims(i)= list(1);
  273.       continue;
  274.     }
  275.       }
  276.       grow, nc_dims, [NC_dim(name="D"+pr1(dims(i)),size=dims(i))];
  277.       dims(i)= numberof(nc_dims);
  278.       changed= 1;
  279.     }
  280.     dims= dims(0:2:-1)-1;
  281.     if (changed) ncf.dims= &nc_dims;
  282.   } else {
  283.     dims= [];
  284.   }
  285.  
  286.   grow, nc_vars, [NC_var(name= name, dimlist= &dims, type= type, len= len)];
  287.   ncf.vars= &nc_vars;
  288. }
  289.  
  290. func nc_enddef(ncf)
  291. /* DOCUMENT f= nc_enddef(ncf)
  292.      creates netCDF file NCF (returned by nc_create), and writes the self-
  293.      descriptive information.  Returns the ordinary Yorick file object
  294.      corresponding to the new file.  You are then free to write variables,
  295.      or use the save or nc_addrec functions.
  296.  
  297.    SEE ALSO: nc_create, nc_addrec, nc_open, nc_attribute, nc_dimsof
  298.  */
  299. {
  300.   /* first, need to fill in addresses */
  301.   vars= *ncf.vars;
  302.   dims= *ncf.dims;
  303.   rec= array(0, numberof(vars));
  304.   if (!is_void(dims)) {
  305.     list= where(dims.size==0);
  306.     if (numberof(list)) {
  307.       /* record variables need to be sorted separately */
  308.       list= list(1)-1;
  309.       for (i=1 ; i<=numberof(vars) ; ++i) {
  310.     dimlist= *vars(i).dimlist;
  311.     if (!is_void(dimlist) && dimlist(1)==list) rec(i)= 1;
  312.       }
  313.     }
  314.   }
  315.   lens= vars.len;
  316.   lens= 4*((lens+3)/4);
  317.   list= where(!rec);
  318.   if (numberof(list)) {
  319.     addrs= lens(list)(cum);
  320.     lens(list)= addrs(1:-1);
  321.     rec_addr= addrs(0);
  322.   } else {
  323.     rec_addr= 0;
  324.   }
  325.   list= where(rec);
  326.   if (numberof(list)) lens(list)= rec_addr + lens(list)(cum)(1:-1);
  327.   vars.address= _nc_desc_len(ncf) + lens;
  328.   ncf.vars= &vars;
  329.  
  330.   f= open(ncf.filename, "w+b");
  331.   xdr_primitives, f;
  332.  
  333.   _nc_declare, f, ncf;
  334.  
  335.   _write, f, 0, (*pointer("CDF\001"))(1:4);
  336.   _write, f, 4, 0;
  337.  
  338.   address= 8;
  339.   NC_WriteArray, f, address, dims;
  340.   NC_WriteArray, f, address, *ncf.attrs;
  341.   NC_WriteArray, f, address, vars;
  342.  
  343.   return f;
  344. }
  345.  
  346. func _nc_desc_len(ncf)
  347. {
  348.   _write= _dummy_write;  /* overlay _write with noop */
  349.   address= 8;
  350.   NC_WriteArray, f, address, *ncf.dims;
  351.   NC_WriteArray, f, address, *ncf.attrs;
  352.   NC_WriteArray, f, address, *ncf.vars;
  353.   return address;
  354. }
  355.  
  356. func _dummy_write(f, a, v)
  357. {
  358.   return sizeof(v);
  359. }
  360.  
  361. func nc_addrec(f, time)
  362. /* DOCUMENT nc_addrec, f, time
  363.        -or- nc_addrec, f
  364.      adds a new record to the netCDF file F at time TIME.
  365.  
  366.    SEE ALSO: nc_create, nc_vardef, nc_enddef
  367.  */
  368. {
  369.   add_record, f, time;
  370.   files= *get_addrs(f)(4);
  371.   _write,f,4, numberof(files==files(0));
  372. }
  373.  
  374. func nc_attribute(attr_name, var_name)
  375. /* DOCUMENT value= nc_attribute(attr_name, var_name)
  376.      gets the value of the netCDF attribute ATTR_NAME associated
  377.      with variable VAR_NAME, or nil if none.  Uses the external
  378.      variable nc_file set by nc_open.
  379.      If VAR_NAME is omitted, ATTR_NAME refers to the whole file,
  380.      and is retrieved (if present) from the nc_file.attrs variable.
  381.  
  382.    SEE ALSO: nc_open, nc_dimsof, nc_create, nc_enddef
  383.  */
  384. {
  385.   if (is_void(var_name)) attrs= nc_file.attrs;
  386.   else {
  387.     attrs= (nc_file.vars.name==var_name);
  388.     if (noneof(attrs)) return [];
  389.     attrs= *nc_file.vars(where(attrs)(0)).attrs;
  390.   }
  391.   if (is_void(attrs)) return [];
  392.   value= (attrs.name==attr_name);
  393.   if (noneof(value)) return [];
  394.   return *attrs(where(value)(0)).data;
  395. }
  396.  
  397. func nc_dimsof(var_name)
  398. /* DOCUMENT def_string= nc_dimsof(var_name)
  399.      returns the dimension list of a netCDF variable VAR_NAME in symbolic
  400.      form, i.e.- using the netCDF dimension names.  This requires the
  401.      nc_file external variable set by nc_open.
  402.  
  403.    SEE ALSO: nc_open, nc_dimsof, nc_create, nc_enddef
  404.  */
  405. {
  406.   dims= (nc_file.vars.name==var_name);
  407.   if (noneof(dims)) return [];
  408.   dims= *nc_file.vars(where(dims)(0)).dimlist;
  409.   result= "(";
  410.   n= numberof(dims);
  411.   for (i=n ; i>0 ; --i) {
  412.     result+= swrite(format="%ld", nc_file.dims(dims(i)).size);
  413.     if (i) result+= ",";
  414.   }
  415.   return result+")";
  416. }
  417.  
  418. /* ------------- data structures --------------------------------- */
  419.  
  420. /* nc_dims is an array of NC_dim */
  421. struct NC_dim {
  422.   string name;  /* represented as NC_string in file */
  423.   long size;  /* length of dimension */
  424. }
  425.  
  426. /* nc_attrs is an array of NC_attr */
  427. struct NC_attr {
  428.   string name;    /* represented as NC_string in file */
  429.   pointer data;   /* represented as NC_array in file */
  430. }
  431.  
  432. /* nc_vars is an array of NC_var */
  433. struct NC_var {
  434.   string name;         /* represented as NC_string in file */
  435.   pointer dimlist;     /* represented as NC_iarray in file, dims index */
  436.   pointer attrs;       /* represented as NC_array in file */
  437.   int type;   /* netCDF data type number */
  438.   long len;   /* bytes */
  439.   long address;
  440. }
  441.  
  442. struct NC_file {
  443.   string filename;     /* for later creation */
  444.   long numrecs;
  445.   pointer dims, attrs, vars;
  446. }
  447.  
  448. /* ------------- object read routines --------------------------------- */
  449.  
  450. func NC_ReadDim(f, &address)
  451. {
  452.   name= NC_ReadString(f, address);
  453.   size= long(0);
  454.   _read, f, address, size;
  455.   address+= 4;
  456.   return NC_dim(name=name, size=size);
  457. }
  458.  
  459. func NC_ReadAttr(f, &address)
  460. {
  461.   name= NC_ReadString(f, address);
  462.   data= &(NC_ReadArray(f, address));
  463.   return NC_attr(name= name, data= data);
  464. }
  465.  
  466. func NC_ReadString(f, &address)
  467. {
  468.   count= long(0);
  469.   _read, f, address, count;
  470.   address+= 4;
  471.   if (count<=0) return string();
  472.   result= array(char, count);
  473.   _read, f, address, result;
  474.   address+= 4*((count+3)/4);
  475.   return string(&result);
  476. }
  477.  
  478. func NC_ReadInts(f, &address)
  479. {
  480.   count= long(0);
  481.   _read, f, address, count;
  482.   address+= 4;
  483.   if (count<=0) return &[];
  484.   result= array(int, count);
  485.   _read, f, address, result;
  486.   address+= 4*count;
  487.   return &result;
  488. }
  489.  
  490. func NC_ReadVar(f, &address)
  491. {
  492.   name= NC_ReadString(f, address);
  493.   dimlist= NC_ReadInts(f, address);
  494.   attrs= &(NC_ReadArray(f, address));   /* must be type NC_attr */
  495.   type= int(0);
  496.   _read, f, address, type;
  497.   address+= 4;
  498.   len= addr= long(0);
  499.   _read, f, address, len;
  500.   address+= 4;
  501.   _read, f, address, addr;
  502.   address+= 4;
  503.   return NC_var(name= name, dimlist= dimlist, attrs= attrs, type= type,
  504.         len= len, address= addr);
  505. }
  506.  
  507. func NC_ReadArray(f, &address)
  508. {
  509.   type= int(0);
  510.   _read, f, address, type;
  511.   address+= 4;
  512.   count= long(0);
  513.   _read, f, address, count;
  514.   address+= 4;
  515.  
  516.   if (type<=0 || type>12 || count<=0)
  517.     return [];
  518.   else if (type<=2)     /* byte (1) or char (2) */
  519.     result= NC_ReadPrim(f, address, char, 1, count);
  520.   else if (type==3)     /* short */
  521.     result= NC_ReadPrim(f, address, short, 2, count);
  522.   else if (type==4)     /* long */
  523.     result= NC_ReadPrim(f, address, long, 4, count);
  524.   else if (type==5)     /* float */
  525.     result= NC_ReadPrim(f, address, float, 4, count);
  526.   else if (type==6)     /* double */
  527.     result= NC_ReadPrim(f, address, double, 8, count);
  528.   else if (type==7)     /* bitfield */
  529.     return [];
  530.   else if (type==8)     /* string */
  531.     result= NC_ReadMulti(f, address, string, count, NC_ReadString);
  532.   else if (type==9)     /* iarray */
  533.     result= NC_ReadMulti(f, address, pointer, count, NC_ReadInts);
  534.   else if (type==10)    /* dimension */
  535.     result= NC_ReadMulti(f, address, NC_dim, count, NC_ReadDim);
  536.   else if (type==11)    /* variable */
  537.     result= NC_ReadMulti(f, address, NC_var, count, NC_ReadVar);
  538.   else if (type==12)    /* attribute */
  539.     result= NC_ReadMulti(f, address, NC_attr, count, NC_ReadAttr);
  540.  
  541.   return result;
  542. }
  543.  
  544. func NC_ReadPrim(f, &address, type, size, count)
  545. {
  546.   result= array(type, count);
  547.   _read, f, address, result;
  548.   address+= 4*((size*count+3)/4);
  549.   return result;
  550. }
  551.  
  552. func NC_ReadMulti(f, &address, type, count, Reader)
  553. {
  554.   result= array(type, count);
  555.   for (i=1 ; i<=count ; ++i) result(i)= Reader(f, address);
  556.   return result;
  557. }
  558.  
  559. /* ------------- object write routines --------------------------------- */
  560.  
  561. func NC_WriteDim(f, &address, dim)
  562. {
  563.   NC_WriteString, f, address, dim.name;
  564.   size= long(0);
  565.   _write, f, address, dim.size;
  566.   address+= 4;
  567. }
  568.  
  569. func NC_WriteAttr(f, &address, attr)
  570. {
  571.   NC_WriteString, f, address, attr.name;
  572.   NC_WriteArray, f, address, *attr.data;
  573. }
  574.  
  575. func NC_WriteString(f, &address, text)
  576. {
  577.   _write, f, address, strlen(text);
  578.   address+= 4;
  579.   if (strlen(text)<=0) return;
  580.   _write, f, address, (*pointer(text))(1:-1);
  581.   address+= 4*((strlen(text)+3)/4);
  582. }
  583.  
  584. func NC_WriteInts(f, &address, vals)
  585. {
  586.   vals= *vals;
  587.   _write, f, address, numberof(vals);
  588.   address+= 4;
  589.   if (numberof(vals)<=0) return;
  590.   _write, f, address, long(vals);
  591.   address+= 4*numberof(vals);
  592. }
  593.  
  594. func NC_WriteVar(f, &address, var)
  595. {
  596.   NC_WriteString, f, address, var.name;
  597.   NC_WriteInts, f, address, var.dimlist;
  598.   NC_WriteArray, f, address, *var.attrs;   /* must be type NC_attr */
  599.   _write, f, address, var.type;
  600.   address+= 4;
  601.   _write, f, address, var.len;
  602.   address+= 4;
  603.   _write, f, address, var.address;
  604.   address+= 4;
  605. }
  606.  
  607. func NC_WriteArray(f, &address, ary)
  608. {
  609.   type= nameof(structof(ary));
  610.   if (type=="int") {
  611.     ary= long(ary);
  612.     type= "long";
  613.   }
  614.   type= where(type==["char","char","short","long","float","double", "",
  615.              "NC_string", "NC_iarray", "NC_dim", "NC_var", "NC_attr"]);
  616.   if (!numberof(type)) {
  617.     if (is_void(ary)) type= 0;
  618.     else error, "illegal netCDF data type: "+nameof(structof(ary));
  619.   } else {
  620.     type= type(1);
  621.   }
  622.   _write, f, address, type;
  623.   address+= 4;
  624.   count= long(0);
  625.   _write, f, address, numberof(ary);
  626.   address+= 4;
  627.  
  628.   if (type<=0 || type>12 || !numberof(ary))
  629.     return;
  630.   else if (type<=2)     /* byte (1) or char (2) */
  631.     NC_WritePrim, f, address, char, 1, ary;
  632.   else if (type==3)     /* short */
  633.     NC_WritePrim, f, address, short, 2, ary;
  634.   else if (type==4)     /* long */
  635.     NC_WritePrim, f, address, long, 4, ary;
  636.   else if (type==5)     /* float */
  637.     NC_WritePrim, f, address, float, 4, ary;
  638.   else if (type==6)     /* double */
  639.     NC_WritePrim, f, address, double, 8, ary;
  640.   else if (type==7)     /* bitfield */
  641.     error, "netCDF bitfield data not supported";
  642.   else if (type==8)     /* string */
  643.     NC_WriteMulti, f, address, string, NC_WriteString, ary;
  644.   else if (type==9)     /* iarray */
  645.     NC_WriteMulti, f, address, pointer, NC_WriteInts, ary;
  646.   else if (type==10)    /* dimension */
  647.     NC_WriteMulti, f, address, NC_dim, NC_WriteDim, ary;
  648.   else if (type==11)    /* variable */
  649.     NC_WriteMulti, f, address, NC_var, NC_WriteVar, ary;
  650.   else if (type==12)    /* attribute */
  651.     NC_WriteMulti, f, address, NC_attr, NC_WriteAttr, ary;
  652. }
  653.  
  654. func NC_WritePrim(f, &address, type, size, data)
  655. {
  656.   _write, f, address, data;
  657.   address+= 4*((size*numberof(data)+3)/4);
  658. }
  659.  
  660. func NC_WriteMulti(f, &address, type, Writer, data)
  661. {
  662.   count= numberof(data);
  663.   for (i=1 ; i<=count ; ++i) Writer, f, address, data(i);
  664. }
  665.  
  666. /* ------------- end of netCDF routines --------------------------------- */
  667.